/*
 * echo_server.c
 *
 *  Created on: Oct 16, 2013
 *      Author: pikro
 */

#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <netinet/in.h>

#include "../include/connexions.h"
#include "../include/echo_server.h"
#include "../include/send.h"

/* Global constants */
#define MAXBUF 1024
#define PORT 33000
#define BACKLOG 1024
#define TAILLEPSEUDO 20

int main(int argc, char *argv[]) {
	int list_s; // listening socket
	int conn_s; // connection socket
	int port; // port number
	char *endptr; // for strtol()
	int ret;
	fd_set readfds;
	int max = 0;
	//int i, k;
	//char* name = malloc(sizeof(char) * 20);
	char* buffer = malloc(sizeof(char) * MAXBUF - 1);
	char* commande = ".list\n";
	char* commandeRequest = ".request";

	listeClients liste = NULL;

	/* Get port number from the command line, and
	 set to default port if no arguments were supplied
	 */

	if (argc == 2) {
		port = strtol(argv[1], &endptr, 0);
		if (*endptr) {
			fprintf(stderr, "ECHOSERV : Invalid port number\n");
			exit(EXIT_FAILURE);
		}
	} else if (argc < 2) {
		port = PORT;
	} else {
		fprintf(stderr, "ECHOSERV: Invalid arguments\n");
		exit(EXIT_FAILURE);
	}

	/* Create the listening socket */

	list_s = create_socket(INADDR_ANY, port);

	max = list_s;

	/* Enter an infinite loop to respond
	 to client requests and echo input */

	while (1) {

		FD_ZERO(&readfds);

		FD_SET(list_s, &readfds);

		/* add socket of each client */

		if (liste != NULL) {
			client* temp = liste;
			while (temp != NULL) {
				FD_SET(temp->socket, &readfds);
				temp = temp->next;
			}
		}

		if (select(max + 1, &readfds, NULL, NULL, NULL) < 0) {
			perror("select");
			return EXIT_FAILURE;
		}

		// 2 possibilités

		if (FD_ISSET(list_s, &readfds)) {

			// Nouvelle connexion sur list_s
			if ((conn_s = accept(list_s, NULL, NULL)) < 0) {
				perror("accept");
				exit(EXIT_FAILURE);
			}

			// On cherche le nouveau max :
			max = conn_s > max ? conn_s : max;

			liste = ajouterClient(liste, conn_s,
					new_client_name(conn_s, liste));

		} else {
			// Activité sur un client déjà connecté

			client* Client = liste;
			while (Client != NULL) {
				if (FD_ISSET(Client->socket, &readfds)) {
					// Si c'est ce client qui est actif, on exécute les actions.
					memset(buffer, 0, MAXBUF - 1);
					ret = readline(Client->socket, buffer, MAXBUF - 1);

					if (ret > 0) {
						fprintf(stdout, "echo>%s", buffer);

						// On regarde si ce n'est pas une commande : ".list"

						if (!strcmp(buffer, commande)) {
							getNames(liste, Client->socket);
						} else if (!strncmp(buffer, commandeRequest, strlen(commandeRequest))) {
							sendRequest(buffer, Client->socket, liste);
						} else if (!strncmp(buffer, "@", 1)) { // Si on envoie le message à une certaine personne.
							sendToName(liste, buffer);
						} else {
							//On envoie à tous, SAUF celui qui a envoyé !

							client* temp = liste;
							while ((temp != NULL)) {
								if (Client->socket != temp->socket) {
									sendline(temp->socket, buffer,
											strlen(buffer));
								}
								temp = temp->next;
							}
						}
					}
					/* Close the connected socket */
					else if (ret <= 0) {
						printf("%s: socket %d hung up\n", argv[0], conn_s);
						if (close(Client->socket) < 0) {
							perror("close");
							exit(EXIT_FAILURE);
						}
						// On retire le client du tableau.
						liste = deleteFromList(Client->socket, liste);
					}

				}
				Client = Client->next;
			}

		}

	}

	return EXIT_SUCCESS;
}

listeClients deleteFromList(int Socket, listeClients liste) {
	client* temp = liste;

	client* delete;
	if (temp->socket == Socket) {
		delete = temp->next;
		free(temp);
		return delete;
	}
	while (temp != NULL) {

		if (temp->next->socket == Socket) {
			temp->next = temp->next->next;

			free(temp->next);
			return liste;
		}
	}
	return liste;
}

listeClients ajouterClient(listeClients liste, int socket, char Pseudo[]) {

	client *new = malloc(sizeof(client));

	new->socket = socket;
	strcpy(new->pseudo, Pseudo);
	new->next = NULL;

	if (liste == NULL)
		return new;
	else {
		client* temp = liste;

		while (temp->next != NULL) {
			temp = temp->next;
		}
		temp->next = new;
		return liste;
	}
}

// Renvoie 1 si le pseudo existe déjà, 0 sinon
int pseudoExiste(listeClients liste, const char *Pseudo) {
	client* temp = liste;

	while (temp != NULL) {
		if (!strcmp(temp->pseudo, Pseudo))
			return 1;
		temp = temp->next;
	}
	return 0;
}
